package org.topteam.oschat.dao;

import static org.iq80.leveldb.impl.Iq80DBFactory.asString;
import static org.iq80.leveldb.impl.Iq80DBFactory.bytes;
import static org.iq80.leveldb.impl.Iq80DBFactory.factory;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.iq80.leveldb.DB;
import org.iq80.leveldb.DBComparator;
import org.iq80.leveldb.DBIterator;
import org.iq80.leveldb.Options;
import org.iq80.leveldb.Range;
import org.iq80.leveldb.WriteBatch;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;

@Component
public class LevelDbDao implements KvDao {

	private Options options = new Options();

	private DB db = null;

	private File dbFile;

	private final static String BASE_PATH = "db";

	public LevelDbDao() {
		DBComparator comparator = new DBComparator() {
			public int compare(byte[] key1, byte[] key2) {
				return new String(key1).compareTo(new String(key2));
			}

			public String name() {
				return "simple";
			}

			public byte[] findShortestSeparator(byte[] start, byte[] limit) {
				return start;
			}

			public byte[] findShortSuccessor(byte[] key) {
				return key;
			}
		};
		options.comparator(comparator);
		options.createIfMissing(true);
		File f = new File(BASE_PATH);
		if (!f.exists()) {
			f.mkdir();
		}
	}

	@Override
	public String get(String key) {
		byte[] value = db.get(bytes(key));
		return asString(value);
	}

	@Override
	public Map<String, String> query(String prefix) {
		DBIterator iterator = db.iterator();
		Map<String, String> values = new HashMap<String, String>();
		for (iterator.seek(bytes(prefix)); iterator.hasNext()
				&& asString(iterator.peekNext().getKey()).startsWith(prefix); iterator
				.next()) {
			String key = asString(iterator.peekNext().getKey());
			String value = asString(iterator.peekNext().getValue());
			values.put(key, value);
		}
		return values;
	}

	@Override
	public void put(String key, String value) {
		db.put(bytes(key), bytes(value));
	}

	@Override
	public void put(Map<String, String> objects) {
		WriteBatch writeBatch = db.createWriteBatch();
		for (String key : objects.keySet()) {
			writeBatch.put(bytes(key), bytes(objects.get(key)));
		}
		db.write(writeBatch);
		try {
			writeBatch.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void batch(Batch batch) {
		WriteBatch writeBatch = db.createWriteBatch();
		batch.batch(writeBatch);
		db.write(writeBatch);
		try {
			writeBatch.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public KvDao open(String _db) {
		try {
			close();
			_db = BASE_PATH + File.separatorChar + _db;
			File dbFile = new File(_db);
			this.dbFile = dbFile;
			db = factory.open(dbFile, options);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return this;
	}

	@Override
	public void remove(String key) {
		db.delete(bytes(key));
	}

	@Override
	public void removeAll() {
		try {
			Options options = new Options();
			factory.destroy(dbFile, options);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void putObj(String key, Object value) {
		put(key, JSON.toJSONString(value));
	}

	@Override
	public <T> void putObj(Map<String, T> objects) {
		WriteBatch writeBatch = db.createWriteBatch();
		for (String key : objects.keySet()) {
			writeBatch.put(bytes(key),
					bytes(JSON.toJSONString(objects.get(key))));
		}
		db.write(writeBatch);
		try {
			writeBatch.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public long size() {
		long[] sizes = db
				.getApproximateSizes(new Range(bytes("0"), bytes("z")));
		return sizes[0];
	}

	@Override
	public String stats() {
		String stats = db.getProperty("leveldb.stats");
		return stats;
	}

	@Override
	public void close() {
		if (db != null) {
			try {
				db.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

}
